package gnss

import (
	"bytes"
	"dgo/goutils/utils"
	"fmt"
	"gitee.com/ymofen/gobase"
	"strings"
)

type EncodeVars struct {
	/** 添加新的属性,记得修改CopyFrom */

	// GRCEJB
	NaviSysFilter string

	// 自定义, B1c和B2a频点
	BDS_B1c_Id byte
	BDS_B2a_Id byte

	BD3_enable   bool
	BDS_SigWl    []byte
	BDS_SigFixed []byte
	BD3_SigWl    []byte
	BD2_enable   bool
	BD2_SigWl    []byte
	GPS_SigWl    []byte
	GLO_SigWl    []byte
	GLO_SigFixed []byte
	GAL_SigWl    []byte
	QZS_SigWl    []byte
	BDS_DelPrns  []byte

	// header
	ClkSteering       byte
	ExternalClk       byte
	SmoothingType     byte
	SmoothingInterval byte

	TagStr            string
	TransVars         *RtcmObsVars // 编码时记录周跳
	Builder           utils.BytesBuilder
	BuilderResetFlag  byte // 0:onRtcmBuf后进行Reset(默认), 1:Clean, 2:不处理
	OutputMessageFunc func(s string, args ...interface{})
}

// 信号集合转成Rnx信号字符
func SigsToRnxID(sigs []byte, navisys byte, sep string) string {
	var sb strings.Builder
	for _, v := range sigs {
		if sb.Len() > 0 {
			sb.WriteString(sep)
		}
		sb.WriteString(SigiRinexCode(navisys, v))
	}
	return sb.String()
}

func FreqBandStr2Sigs(navsys byte, sigstr string) (sigs []byte) {
	strs := strings.Split(sigstr, ",")
	for _, s0 := range strs {
		s0 = gobase.Trim(s0)
		if len(s0) > 0 {
			sigs = append(sigs, FreqBand2Sigs(navsys, gobase.Trim(s0))...)
		}
	}
	return sigs
}

func FreqBand2Sigs(navsys byte, fid string) (sigs []byte) {
	switch navsys {
	case SYS_BD:
		switch fid {
		case "B1", "B1I":
			sigs = append(sigs, RTCM_SIG_BDS_B1_2I, RTCM_SIG_BDS_B1_2Q, RTCM_SIG_BDS_B1_2X)
		case "B3", "B3I":
			sigs = append(sigs, RTCM_SIG_BDS_B3_6I, RTCM_SIG_BDS_B3_6Q, RTCM_SIG_BDS_B3_6X)
		case "B2", "B2I":
			sigs = append(sigs, RTCM_SIG_BDS_B2_7I, RTCM_SIG_BDS_B2_7Q, RTCM_SIG_BDS_B2_7X)
		case "B2a", "B2A":
			sigs = append(sigs, RTCM_SIG_BDS_B2a_5D, RTCM_SIG_BDS_B2a_5P, RTCM_SIG_BDS_B2a_5X)
		case "B2b", "B2B":
			sigs = append(sigs, RTCM_SIG_BDS_B2b_7D)
		case "B1C", "B1c":
			sigs = append(sigs, RTCM_SIG_BDS_B1C_1D, RTCM_SIG_BDS_B1C_1P, RTCM_SIG_BDS_B1C_1X)
		}
		return
	case SYS_GPS:
		switch fid {
		case "L1":
			sigs = append(sigs, RTCM_SIG_GPS_L1_1C, RTCM_SIG_GPS_L1_1P, RTCM_SIG_GPS_L1_1W, RTCM_SIG_GPS_L1_1S, RTCM_SIG_GPS_L1_1L, RTCM_SIG_GPS_L1_1X)
		case "L2":
			sigs = append(sigs, RTCM_SIG_GPS_L2_2C, RTCM_SIG_GPS_L2_2P, RTCM_SIG_GPS_L2_2W, RTCM_SIG_GPS_L2_2S, RTCM_SIG_GPS_L2_2L, RTCM_SIG_GPS_L2_2X)
		case "L5":
			sigs = append(sigs, RTCM_SIG_GPS_L5_5I, RTCM_SIG_GPS_L5_5Q, RTCM_SIG_GPS_L5_5X)
		}
		return
	case SYS_GLO:
		switch fid {
		case "G1":
			sigs = append(sigs, RTCM_SIG_GLO_G1_1C, RTCM_SIG_GLO_G1_1P)
		case "G2":
			sigs = append(sigs, RTCM_SIG_GLO_G2_2C, RTCM_SIG_GLO_G2_2P)
		}
		return
	case SYS_GAL:
		switch fid {
		case "E1":
			sigs = append(sigs, RTCM_SIG_GAL_E1_1C, RTCM_SIG_GAL_E1_1A, RTCM_SIG_GAL_E1_1B, RTCM_SIG_GAL_E1_1X, RTCM_SIG_GAL_E1_1Z)
		case "E5A", "E5a":
			sigs = append(sigs, RTCM_SIG_GAL_E5a_5I, RTCM_SIG_GAL_E5a_5Q, RTCM_SIG_GAL_E5a_5X)
		case "E5B", "E5b":
			sigs = append(sigs, RTCM_SIG_GAL_E5b_7I, RTCM_SIG_GAL_E5b_7Q, RTCM_SIG_GAL_E5b_7X)
		case "E6":
			sigs = append(sigs, RTCM_SIG_GAL_E6_6C, RTCM_SIG_GAL_E6_6A, RTCM_SIG_GAL_E6_6B, RTCM_SIG_GAL_E6_6X, RTCM_SIG_GAL_E6_6Z)
		case "E5AB", "E5ab":
			sigs = append(sigs, RTCM_SIG_GAL_E5ab_8I, RTCM_SIG_GAL_E5ab_8Q, RTCM_SIG_GAL_E5ab_8X)
		}
		return
	case SYS_QZS:
		switch fid {
		case "L1":
			sigs = append(sigs, RTCM_SIG_QZS_L1_1C, RTCM_SIG_QZS_L1_1S, RTCM_SIG_QZS_L1_1S, RTCM_SIG_QZS_L1_1L, RTCM_SIG_QZS_L1_1X)
		case "L2":
			sigs = append(sigs, RTCM_SIG_QZS_L2_2S, RTCM_SIG_QZS_L2_2L, RTCM_SIG_QZS_L2_2X)
		case "L5":
			sigs = append(sigs, RTCM_SIG_QZS_L5_5I, RTCM_SIG_QZS_L5_5Q, RTCM_SIG_QZS_L5_5X)
		case "L6":
			sigs = append(sigs, RTCM_SIG_QZS_L6_6L, RTCM_SIG_QZS_L6_6S, RTCM_SIG_QZS_L6_6X)
		}
		return
	}

	return
}

func NewEncodeVars() *EncodeVars {
	rval := &EncodeVars{
		BD3_enable: true,
		BD2_enable: true,
	}
	return rval
}

func (this *EncodeVars) Clone() *EncodeVars {
	obj := &EncodeVars{}
	obj.CopyFrom(this)
	return obj
}

func (this *EncodeVars) Reset() {
	this.BD3_enable = true
	this.BD2_enable = true
	this.BDS_SigWl = make([]byte, 0)
	this.BDS_SigFixed = make([]byte, 0)
	this.BD2_SigWl = make([]byte, 0)
	this.BD3_SigWl = make([]byte, 0)
	this.GPS_SigWl = make([]byte, 0)
	this.GLO_SigWl = make([]byte, 0)
	this.GLO_SigFixed = make([]byte, 0)
	this.GAL_SigWl = make([]byte, 0)
	this.QZS_SigWl = make([]byte, 0)
	this.BDS_DelPrns = make([]byte, 0)
	this.BDS_B1c_Id = 0
	this.BDS_B2a_Id = 0
}

func (this *EncodeVars) CreateTransVars() {
	this.TransVars = &RtcmObsVars{}
}

func (this *EncodeVars) Status() string {
	return fmt.Sprintf("sys:%s, bd3:%v", this.NaviSysFilter, this.BD3_enable)
}

func (this *EncodeVars) CopyFrom(src *EncodeVars) {
	this.NaviSysFilter = src.NaviSysFilter
	this.BD3_enable = src.BD3_enable
	this.BD2_enable = src.BD2_enable
	this.BD3_SigWl = src.BD3_SigWl
	this.BD2_SigWl = src.BD2_SigWl
	this.BDS_DelPrns = src.BDS_DelPrns
	this.BDS_SigWl = src.BDS_SigWl
	this.GPS_SigWl = src.GPS_SigWl
	this.GLO_SigWl = src.GLO_SigWl
	this.GLO_SigFixed = src.GLO_SigFixed
	this.GAL_SigWl = src.GAL_SigWl
	this.QZS_SigWl = src.QZS_SigWl
	this.BuilderResetFlag = src.BuilderResetFlag
}

func (this *EncodeVars) ResetBuilder() {
	if this.BuilderResetFlag == 0 {
		this.Builder.Reset()
	} else if this.BuilderResetFlag == 1 {
		this.Builder.Cleanup()
	} else if this.BuilderResetFlag == 2 {
		// none
	} else {
		this.Builder.Reset()
	}
}

func (this *EncodeVars) SetBdsDelPrnlst(s string) {
	lst := strings.Split(s, ",")
	prns := make([]byte, 0, len(lst))
	for i := 0; i < len(lst); i++ {
		prn := utils.StrToIntDef(lst[i], 0)
		if prn > MIN_BD_PRN_ID && prn < MAX_BD_PRN_ID {
			prns = append(prns, byte(prn))
		}
	}
	this.BDS_DelPrns = prns
}

// C2=B1,B2,B3,B2a,B1C&C3=&G=&R=&E=&J=&SYS=GRCEJ
func (this *EncodeVars) LoadFromUrlEncode(s string) {
	confMap := gobase.NewStrMap()
	confMap.URLFormDecode(s)
	syslst := make([]byte, 0, 5)
	{
		wl := make([]byte, 0, 12)
		c3 := confMap.StringByName("C3", "")
		if len(c3) == 0 {
			this.BD3_enable = false
		} else {
			this.BD3_enable = true
			strs := strings.Split(c3, ",")
			for _, s0 := range strs {
				s0 = gobase.Trim(s0)
				if len(s0) > 0 {
					sigId := SigRinexID2SigV(SYS_BD, s0)
					if sigId != 0 {
						wl = append(wl, sigId)
					} else {
						wl = append(wl, FreqBand2Sigs(SYS_BD, gobase.Trim(s0))...)
					}
				}
			}
		}
		this.BD3_SigWl = wl
	}

	{
		wl := make([]byte, 0, 12)
		str := confMap.StringByName("C2", "")
		if len(str) == 0 {
			this.BD2_enable = false
		} else {
			this.BD2_enable = true
			strs := strings.Split(str, ",")
			for _, s0 := range strs {
				s0 = gobase.Trim(s0)
				sigId := SigRinexID2SigV(SYS_BD, s0)
				if sigId != 0 {
					wl = append(wl, sigId)
				} else {
					wl = append(wl, FreqBand2Sigs(SYS_BD, gobase.Trim(s0))...)
				}
			}
		}

		this.BD2_SigWl = wl
	}

	if len(this.BD3_SigWl) > 0 || len(this.BD2_SigWl) > 0 {
		wl := make([]byte, 0, 12)
		wl = append(wl, this.BD2_SigWl...)
		for _, v := range this.BD3_SigWl {
			if bytes.IndexByte(wl, v) == -1 {
				wl = append(wl, this.BD3_SigWl...)
			}
		}

		this.BDS_SigWl = wl
		syslst = append(syslst, 'C')
	}

	{
		wl := make([]byte, 0, 12)
		str := confMap.StringByName("G", "")
		if len(str) > 0 {
			strs := strings.Split(str, ",")
			for _, s0 := range strs {
				s0 = gobase.Trim(s0)
				sigId := SigRinexID2SigV(SYS_GPS, s0)
				if sigId != 0 {
					wl = append(wl, sigId)
				} else {
					wl = append(wl, FreqBand2Sigs(SYS_GPS, gobase.Trim(s0))...)
				}
			}
		}
		this.GPS_SigWl = wl
		if len(wl) > 0 {
			syslst = append(syslst, 'G')
		}
	}

	{
		wl := make([]byte, 0, 12)
		str := confMap.StringByName("R", "")
		if len(str) > 0 {
			strs := strings.Split(str, ",")
			for _, s0 := range strs {
				s0 = gobase.Trim(s0)
				sigId := SigRinexID2SigV(SYS_GLO, s0)
				if sigId != 0 {
					wl = append(wl, sigId)
				} else {
					wl = append(wl, FreqBand2Sigs(SYS_GLO, gobase.Trim(s0))...)
				}
			}
		}
		this.GLO_SigWl = wl
		if len(wl) > 0 {
			syslst = append(syslst, 'R')
		}
	}

	{
		wl := make([]byte, 0, 12)
		str := confMap.StringByName("E", "")
		if len(str) > 0 {
			strs := strings.Split(str, ",")
			for _, s0 := range strs {
				s0 = gobase.Trim(s0)
				sigId := SigRinexID2SigV(SYS_GAL, s0)
				if sigId != 0 {
					wl = append(wl, sigId)
				} else {
					wl = append(wl, FreqBand2Sigs(SYS_GAL, gobase.Trim(s0))...)
				}
			}
		}
		this.GAL_SigWl = wl
		if len(wl) > 0 {
			syslst = append(syslst, 'E')
		}
	}

	{
		wl := make([]byte, 0, 12)
		str := confMap.StringByName("J", "")
		if len(str) > 0 {
			strs := strings.Split(str, ",")
			for _, s0 := range strs {
				s0 = gobase.Trim(s0)
				sigId := SigRinexID2SigV(SYS_QZS, s0)
				if sigId != 0 {
					wl = append(wl, sigId)
				} else {
					wl = append(wl, FreqBand2Sigs(SYS_QZS, gobase.Trim(s0))...)
				}
			}
		}
		this.QZS_SigWl = wl
		if len(wl) > 0 {
			syslst = append(syslst, 'J')
		}
	}

	s = confMap.StringByName("SYS", "")
	if len(s) > 0 {
		this.NaviSysFilter = s
	} else {
		this.NaviSysFilter = string(syslst)
	}

	s = confMap.StringByName("BD2", "")
	if len(s) > 0 {
		this.BD2_enable = gobase.TryStrToBool(s, this.BD2_enable)
	}

	s = confMap.StringByName("BD3", "")
	if len(s) > 0 {
		this.BD3_enable = gobase.TryStrToBool(s, this.BD3_enable)
	}
}

func (this *EncodeVars) String() string {
	return fmt.Sprintf("BD3:%d, BDS_Del_Prns:%v, BDS_Sign:%v,  GPS_Sign:%v, GAL_Sign:%v, GLO_Sign:%v", utils.Bool2Byte(this.BD3_enable), this.BDS_DelPrns,
		this.BDS_SigWl, this.GPS_SigWl, this.GAL_SigWl, this.GLO_SigWl)
}

func (this *EncodeVars) outputMessage(s string, args ...interface{}) {
	if this.OutputMessageFunc != nil {
		this.OutputMessageFunc(s, args...)
	}
}
